package com.block.source.service.impl;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.block.common.constant.RedisKeyConstant;
import com.block.common.enums.InfoTypeEnum;
import com.block.common.exception.NotifyExceptionService;
import com.block.common.model.dto.RequestTimeDTO;
import com.block.common.model.dto.SourceCountDTO;
import com.block.common.model.vo.ResultInfoVO;
import com.block.common.mq.MqSourceCountSendService;
import com.block.common.service.ISourceDataCountInfoService;
import com.block.common.service.ISystemRequestTimeRecordService;
import com.block.common.service.LogFactory;
import com.block.common.service.merchant.IMerchantThirdBaseInfoService;
import com.block.db.entity.*;
import com.block.db.mapper.MerchantThirdProtocolInfoMapper;
import com.block.match.enums.OrderStatusEnum;
import com.block.match.model.dto.SourceMatchResultDTO;
import com.block.match.service.IDataMatchOrderItemService;
import com.block.match.service.ISourceMatchHandleService;
import com.block.merchant.enums.CheckStatusEnum;
import com.block.merchant.model.dto.CheckSourceTypeDTO;
import com.block.source.model.param.UserDataParam;
import com.block.source.service.*;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

@Service
public class SourceCheckUserService implements ISourceCheckUserService {

    private static final Logger log = LogFactory.getSourceLog();
    private static final Logger unionLog = LogFactory.getUnionLog();

    @Autowired
    NotifyExceptionService notifyExceptionService;
    @Autowired
    ISourceMemberDataInfoTempService sourceMemberDataInfoTempService;
    @Autowired
    ISourceMemberDataInfoHistoryService sourceMemberDataInfoHistoryService;
    @Autowired
    ISourceMatchHandleService sourceMatchHandleService;
    @Autowired
    ISourcePushResultInfoService sourcePushResultInfoService;
    @Autowired
    ISystemRequestTimeRecordService systemRequestTimeRecordService;
    @Autowired
    MqSourceCountSendService mqSourceCountSendService;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    IDataMatchOrderItemService dataMatchOrderItemService;
    @Autowired
    IMerchantThirdBaseInfoService merchantThirdBaseInfoService;
    @Resource
    MerchantThirdProtocolInfoMapper merchantThirdProtocolInfoMapper;
    @Autowired
    ISourceDataCountInfoService sourceDataCountInfoService;


    @Override
    public ResultInfoVO sourceCheckUser1(SourceBaseInfo sourceBaseInfo, UserDataParam userDataParam, ChannelBaseInfo channelBaseInfo, ApplicationInfo applicationInfo){

        try {
            CountDownLatch countDownLatch = new CountDownLatch(1);
            Future<ResultInfoVO> merchantInfoFuture = ThreadUtil.execAsync(new Callable<ResultInfoVO>(){
                @Override
                public ResultInfoVO call() throws Exception {

                    ResultInfoVO resultInfoVO = sourceCheckUser(sourceBaseInfo, userDataParam, channelBaseInfo, applicationInfo);
                    countDownLatch.countDown();
                    return resultInfoVO;
                }

            });

            // 撞库时间
            long  checkTimeout = sourceBaseInfo.getCheckTimeout() - 250;
            countDownLatch.await(checkTimeout, TimeUnit.MILLISECONDS);

            if (merchantInfoFuture.isDone()){
                ResultInfoVO resultInfoVO = merchantInfoFuture.get();
                return resultInfoVO;
            }

//            System.out.println("撞库超时=====================");

        }catch (Exception exception){
            exception.printStackTrace();
            String message = StrUtil.format("【channelCode】{}【channelName】{}【data】{}",
            sourceBaseInfo.getChannelCode(), sourceBaseInfo.getChannelName(), JSON.toJSONString(userDataParam));
            notifyExceptionService.notifyNoticeInfo("SOURCE_CHECK_EXC", "撞库操作异常", message, exception);

        }
        return ResultInfoVO.error("撞库失败");
    }

    @Override
    public ResultInfoVO sourceCheckUser(SourceBaseInfo sourceBaseInfo, UserDataParam userDataParam, ChannelBaseInfo channelBaseInfo, ApplicationInfo applicationInfo) throws Exception {

        try {

            CheckSourceTypeDTO checkSourceTypeDTO = new CheckSourceTypeDTO();
            String checkType = channelBaseInfo.getCheckType();
            checkSourceTypeDTO.setCheckType(checkType);
            // 同一个渠道，相同的手机号md5 ，6个小时内只能撞库一次，，，撞库失败也只能撞一次
            //String  mobileMd5 = userDataParam.getMobileMd5();
            String channelCode = sourceBaseInfo.getChannelCode();
            if (!ObjectUtil.equal(CheckStatusEnum.MOBILEMASK.getValue(), checkType)){
//                //掩码
//                String mobileMask = userDataParam.getMobileMask();
//                checkSourceTypeDTO.setMobileMask(mobileMask);
//                String redisKey = StrUtil.format(RedisKeyConstant.SOURCE_CHECK_MOBILEMASK_CHANNEL, channelCode, mobileMask);
//                // 查询缓存是否有
//                boolean setIfAbsent = stringRedisTemplate.opsForValue().setIfAbsent(redisKey, "0", 6, TimeUnit.HOURS);
//                if (!setIfAbsent){
//                    // 6小时之内不能重复撞库
//                    return ResultInfoVO.error("同一手机号6小时内不能重复撞库");
//                }

                //MD5
                // 同一个渠道，相同的手机号md5 ，6个小时内只能撞库一次，，，撞库失败也只能撞一次
                String  mobileMd5 = userDataParam.getMobileMd5();
                checkSourceTypeDTO.setMobileMd5(mobileMd5);
                String redisKey = StrUtil.format(RedisKeyConstant.SOURCE_CHECK_MOBILEMD5_CHANNEL, channelCode, mobileMd5);
                // 查询缓存是否有
                boolean setIfAbsent = stringRedisTemplate.opsForValue().setIfAbsent(redisKey, "0", 6, TimeUnit.HOURS);
                if (!setIfAbsent){
                    // 6小时之内不能重复撞库
                    return ResultInfoVO.error("同一手机号6小时内不能重复撞库");
                }
            }
            //String redisKey = StrUtil.format(RedisKeyConstant.SOURCE_CHECK_MOBILEMD5_CHANNEL, channelCode, mobileMd5);
            // 查询缓存是否有
//            boolean setIfAbsent = stringRedisTemplate.opsForValue().setIfAbsent(redisKey, "0", 6, TimeUnit.HOURS);
//            if (!setIfAbsent){
//                // 6小时之内不能重复撞库
//                return ResultInfoVO.error("同一手机号6小时内不能重复撞库");
//            }

            JSONObject returnObject = new JSONObject();

            log.info("【线索撞库】【channelCode】{}【channelName】{}【UserDataParam】{}",
                    channelBaseInfo.getChannelCode(), channelBaseInfo.getChannelName(), JSON.toJSONString(userDataParam));

            // 计时开始
            Long time0 = System.currentTimeMillis();

            // todo 线索统计 异步
            SourceCountDTO sourceCountDTO = new SourceCountDTO(channelBaseInfo.getChannelCode(), channelBaseInfo.getChannelName());
            // 撞库统计
            sourceCountDTO.setCheckNumber(1);
            sourceDataCountInfoService.saveSourceCountInfo(sourceCountDTO);
//            mqSourceCountSendService.sourceDataCountSend(sourceCountDTO);

            // 撞库数据统计
            Long time1 = System.currentTimeMillis();

            // 手机号md5 撞库
            Long time2 = System.currentTimeMillis();

            // 临时数据入库
            ResultInfoVO resultInfoVO = sourceMemberDataInfoTempService.saveMemberData(userDataParam, channelBaseInfo, applicationInfo);
            if (!resultInfoVO.isSuccess()){

                return resultInfoVO;
            }

            // 临时数据入库
            Long time3 = System.currentTimeMillis();

            // 订单号
            JSONObject data = resultInfoVO.getData();
            SourceMemberDataInfoTemp dataInfoTemp = (SourceMemberDataInfoTemp) data.get("dataInfo");

             sourceMemberDataInfoHistoryService.saveMemberData(dataInfoTemp);
            // 根据临时结果去进行撞库操作
            SourceMatchResultDTO resultDTO = sourceMatchHandleService.toMatchMerchant(sourceBaseInfo, dataInfoTemp,checkSourceTypeDTO);
            returnObject.put("md5List", resultDTO.getMd5List());
            // 线索撞库完成
            Long time4 = System.currentTimeMillis();

            long execTime = time4 - time3;
            if (execTime > 2000){
                RequestTimeDTO timeDTO = new RequestTimeDTO();
                timeDTO.setBizType("SOURCE_CHECK_HANDLE");
                timeDTO.setBizName("线索撞库流程");
                timeDTO.setExecTime(String.valueOf(execTime));
                timeDTO.setLineTime("500");
                timeDTO.setParamInfo(StrUtil.format("【channelCode】{}【channelName】{}", sourceBaseInfo.getChannelCode(), sourceBaseInfo.getChannelName()));
				timeDTO.setResultInfo(JSON.toJSONString(resultDTO));
                timeDTO.setRemarkInfo(StrUtil.format("【resultNo】{}", dataInfoTemp.getId()));
                systemRequestTimeRecordService.saveRequestTime(timeDTO);
            }

        MerchantThirdBaseInfo thirdBaseInfo = null;
            log.info("【线索撞库】撞库完成【resultDTO】{}", JSON.toJSONString(resultDTO));
            if (resultDTO.isSuccess()){
                // 撞库成功，保存撞库结果数据
                SourcePushResultInfo sourcePushResultInfo = sourcePushResultInfoService.savePushResultInfo(dataInfoTemp, resultDTO, sourceBaseInfo);
                if (!OrderStatusEnum.PRICE_LOW.getValue().equals(sourcePushResultInfo.getStatus())){
                    SourceCountDTO dto = new SourceCountDTO(channelBaseInfo.getChannelCode(), channelBaseInfo.getChannelName());
                    //sourceCountDTO.setCheckNumber(null);
                    dto.setCheckPassNumber(1);
                    mqSourceCountSendService.sourceDataCountSend(dto);

                    returnObject.put("status", "000");
                    returnObject.put("remark", "撞库通过，可以推送");
                    returnObject.put("orderNo", sourcePushResultInfo.getId()+"");

                    // 如果机构类型为apis 则，查询机构商户的信息
                    if (StrUtil.equals(InfoTypeEnum.APIS.getValue(), sourcePushResultInfo.getInfoType())){
                        String orderItemNo = sourcePushResultInfo.getOrderItemNo();
                        DataMatchOrderItem matchOrderItem = dataMatchOrderItemService.getById(orderItemNo);
                        thirdBaseInfo = merchantThirdBaseInfoService.selectEntityByOrderNo(matchOrderItem.getPushId());
                        List<MerchantThirdProtocolInfo> thirdProtocolInfoList = merchantThirdProtocolInfoMapper.selectListByThirdId(thirdBaseInfo.getId());

                        returnObject.put("thirdBaseInfo", thirdBaseInfo);
                        returnObject.put("thirdProtocolInfoList", thirdProtocolInfoList);
                    }
                }else {
                    returnObject.put("status", "999");
                    returnObject.put("remark", "撞库失败");
                }
            }else{
                returnObject.put("status", "999");
                returnObject.put("remark", "撞库失败");
            }

            // 数据返回（成功/失败）
            Long time5 = System.currentTimeMillis();

            if (time4-time0 > 2000){
                RequestTimeDTO timeDTO = new RequestTimeDTO();
                timeDTO.setBizType("SOURCE_CHECK_RECORD");
                timeDTO.setBizName("线索撞库流程分步记录");
                timeDTO.setExecTime(String.valueOf(time4- time0));
                timeDTO.setParamInfo(StrUtil.format("【channelCode】{}【channelName】{}【mobileMd5】{}", sourceBaseInfo.getChannelCode(), sourceBaseInfo.getChannelName(), dataInfoTemp.getMobileMd5()));
                timeDTO.setResultInfo(JSON.toJSONString(resultDTO));
                timeDTO.setRemarkInfo(StrUtil.format("【time1-time0】{}【time2-time1】{}【time3-time2】{}【time4-time3】{}【time5-time4】{}",
                        time1-time0, time2-time1, time3-time2, time4-time3, time5-time4));
                systemRequestTimeRecordService.saveRequestTime(timeDTO);
            }

            return ResultInfoVO.success(returnObject);
        }catch (Exception exception){
            String message = StrUtil.format("【channelCode】{}【channelName】{}【data】{}",
                    sourceBaseInfo.getChannelCode(), sourceBaseInfo.getChannelName(), JSON.toJSONString(userDataParam));
            notifyExceptionService.notifyNoticeInfo("SOURCE_CHECK_EXC111", "撞库操作异常", message, exception);
            throw exception;
        }
    }

}
